package customer

import (
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/config"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/dao"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/entity"
	"gitee.com/baal1990/gitee.com/baal1990/cmd/server/app/global"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/cache"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/crypto"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/email"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/model"
	"gitee.com/baal1990/gitee.com/baal1990/pkg/util"
	"github.com/gin-gonic/gin"
	"github.com/gomodule/redigo/redis"
	uuid "github.com/satori/go.uuid"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"strings"
	"time"
)

//********************************************
//Func  ：查询所有客户类型
//Method：GET
//Usage ：留言板页面-用户注册-下拉选择证件类型
//********************************************
func QueryAllCustomerTypes(c *gin.Context) {
	types := []*entity.CustomerType{}
	type1 := &entity.CustomerType{
		Id:   0,
		Type: "个人投资者",
	}
	types = append(types, type1)

	type2 := &entity.CustomerType{
		Id:   1,
		Type: "机构投资者",
	}
	types = append(types, type2)

	type3 := &entity.CustomerType{
		Id:   2,
		Type: "产业客户",
	}
	types = append(types, type3)

	type4 := &entity.CustomerType{
		Id:   3,
		Type: "期货公司",
	}
	types = append(types, type4)

	type5 := &entity.CustomerType{
		Id:   4,
		Type: "信息商",
	}
	types = append(types, type5)

	type6 := &entity.CustomerType{
		Id:   5,
		Type: "其他",
	}
	types = append(types, type6)
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", types))
}

//********************************************
//Func  ：查询所有证件类型
//Method：GET
//Usage ：留言板页面-用户注册-下拉选择证件类型
//********************************************
func QueryAllCertificateTypes(c *gin.Context) {
	types := []*entity.CertificateTypes{}
	for i := 0; i <= int(entity.ECertificateType_Other); i++ {
		type111 := &entity.CertificateTypes{
			Id: i,
		}
		if i == 1 {
			type111.Type = "身份证"
		} else if i == 2 {
			type111.Type = "护照"
		} else if i == 3 {
			type111.Type = "户口本"
		} else if i == 4 {
			type111.Type = "军官证"
		} else if i == 5 {
			type111.Type = "警官证"
		} else if i == 6 {
			type111.Type = "士兵证"
		} else if i == 7 {
			type111.Type = "港澳地区居民来往内地通行证"
		} else if i == 8 {
			type111.Type = "台胞证"
		} else if i == 9 {
			type111.Type = "港澳台居民居住证"
		} else if i == 10 {
			type111.Type = "外国护照"
		} else if i == 11 {
			type111.Type = "其它"
		}
		types = append(types, type111)
	}
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", types))
}

//********************************************
//Func  ：查询用户列表，分页查询。
//Method：GET
//Usage ：
//********************************************
func QueryAllCustomers(c *gin.Context) {

	pageSize, err1 := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	if err1 != nil {
		global.Log.Error("QueryAllCustomers input param pagesize error:", zap.Any("error", err1))
		c.JSON(http.StatusOK, model.Fail("请求参数错误"))
		return
	}

	page, err2 := strconv.Atoi(c.DefaultQuery("page", "1"))
	if err2 != nil {
		global.Log.Error("QueryAllCustomers input param page error:", zap.Any("error", err2))
		c.JSON(http.StatusOK, model.Fail("请求参数错误"))
		return
	}

	mobile := c.DefaultQuery("operatorMobile", "") //邮件
	if mobile == "" {
		global.Log.Info("QueryAllCustomers querier.")
	}

	total, customerList, err := dao.QueryCustomList(page, pageSize, mobile)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("查询失败"))
		global.Log.Info("QueryCustomList failed ", zap.Any("error:", err))
		return
	}

	type CustomerList struct { //列表查询输出
		Total int64
		List  []*entity.Customer
	}

	output := &CustomerList{
		Total: total,
		List:  customerList,
	}

	global.Log.Info("QueryAllOperators success, output:", zap.Any("custerm list:", output))
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", output))
}

//********************************************
//Func  ：查询指定用户详情
//Method：GET
//Param：operatorId操作员ID
//Usage ：
//********************************************
func QueryCustomerDetail(c *gin.Context) {

	customerId, err := strconv.ParseInt(c.DefaultPostForm("customerId", ""), 10, 64)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("未指定用户信息"))
		return
	}

	mobile := c.DefaultQuery("mobile", "")
	if customerId <= 0 && mobile == "" {
		global.Log.Error("QueryOperatorDetail query customer detail failed, Error")
		c.JSON(http.StatusOK, model.Fail("未指定操作员信息"))
		return
	}

	customer, err := dao.QueryCustomerDetail(mobile, customerId)
	if err != nil {
		global.Log.Error("dao.QueryOperatorDetail failed", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("未找到指定操作员信息"))
		return
	}
	customer.Password = ""
	global.Log.Info("QueryOperatorDetail operator info:", zap.Any("customer:", customer))
	c.JSON(http.StatusOK, model.SuccessObject("操作成功", customer))
}

//********************************************
//Func  ：添加注册新用户
//Method：POST
//Usage ：留言板页面-用户注册-提交注册信息
//********************************************
func AddNewCustomer(c *gin.Context) {
	var err error
	var customer entity.Customer

	customer.Name = strings.TrimSpace(c.DefaultPostForm("name", ""))
	if len(customer.Name) <= 0 {
		c.JSON(http.StatusOK, model.Fail("姓名不能为空"))
		return
	}
	customer.Password = c.DefaultPostForm("password", "")
	if len(customer.Password) <= 0 {
		c.JSON(http.StatusOK, model.Fail("密码不能为空"))
		return
	}

	cryptoInstance, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if ok {
		pwdByte, err := cryptoInstance.DecryptWithBase64(customer.Password, global.ASymmetricPrivateKey)
		if err != nil {
			customer.Password = string(pwdByte)
			//此处Password已经变更为明文
		}
	} else {
		global.Log.Error("global.Crypto.Digest CryptoSM2 error,no found")
	}
	cryptoInstance2, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if ok {
		customer.Password, err = cryptoInstance2.Sum([]byte(customer.Password))
		//此处Password已经变更为密码摘要
	} else {
		global.Log.Error("global.Crypto.Digest CryptoSM3 error,no found")
	}

	customer.Mobile = strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if len(customer.Mobile) <= 0 {
		c.JSON(http.StatusOK, model.Fail("手机号码不能为空"))
		return
	}
	customer.CreatedAt = time.Now()
	//customer.Email = c.DefaultPostForm("email", "")	//email需要绑定，不能直接用
	customer.Telephone = c.DefaultPostForm("telephone", "")
	customer.IsOrganization, err = strconv.ParseBool(c.DefaultPostForm("isOrganization", "false"))

	if customer.IsOrganization { //组织
		customer.IndustryInvolved = c.DefaultPostForm("industryInvolved", "") //所属行业
		customer.CeoName = c.DefaultPostForm("ceoname", "")                   //法人代表
		customer.CertUrl = c.DefaultPostForm("certurl", "")                   //上传件Url
		//todo 根据是否必须要求上述项目，如果需要则在此加上判断
	} else { //个人
		certTypeStr := c.DefaultPostForm("certType", "")
		typeId, err := strconv.Atoi(certTypeStr)
		if err != nil {
			c.JSON(http.StatusOK, model.Fail("证件类型不支持"))
			return
		}
		customer.CertType = entity.ECertificateType(typeId)
	}
	customer.IdNumber = c.DefaultPostForm("idNumber", "") //个人证件id或组织机构代
	customer.Status = 0                                   //正常
	customer.Id = util.GenSnowflakeInt64()

	global.Log.Info("AddNewCustomer: pareper to insert", zap.Any("customer:", customer))
	newcuster, err := dao.InsertCustomer(customer)
	if err != nil {
		if strings.Contains(err.Error(), "Duplicate") {
			c.JSON(http.StatusOK, model.Fail("手机号码已被占用"))
		} else {
			c.JSON(http.StatusOK, model.Fail("操作失败"))
		}
		global.Log.Info("AddNewOperator InsertCustomer failed.", zap.Any("error", err))
	} else {
		c.JSON(http.StatusOK, model.SuccessObject("操作成功", customer.Id)) //返回新增用户的id
		global.Log.Info("AddNewOperator success.", zap.Any("new operator", newcuster))
	}
}

//********************************************
//Func  ：更新用户信息
//Method：POST
//Usage ：留言板页面-用户注册-信息变更
//********************************************
func UpdateCustomer(c *gin.Context) {
	var err error
	var customer entity.Customer

	customer.Id, err = strconv.ParseInt(c.DefaultPostForm("customerId", ""), 10, 64)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("未指明用户ID"))
		return
	}

	customer.Name = strings.TrimSpace(c.DefaultPostForm("name", ""))
	if len(customer.Name) <= 0 {
		c.JSON(http.StatusOK, model.Fail("姓名不能为空"))
		return
	}

	customer.Mobile = strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if len(strings.TrimSpace(customer.Mobile)) <= 0 {
		c.JSON(http.StatusOK, model.Fail("手机号码不能为空"))
		return
	}

	customer.Telephone = c.DefaultPostForm("telephone", "")
	customer.IsOrganization, err = strconv.ParseBool(c.DefaultPostForm("isOrganization", "false"))

	if customer.IsOrganization { //组织
		customer.IndustryInvolved = c.DefaultPostForm("industryInvolved", "") //所属行业
		customer.CeoName = c.DefaultPostForm("ceoname", "")                   //法人代表
		customer.CertUrl = c.DefaultPostForm("certurl", "")                   //上传件Url
		//todo 根据是否必须要求上述项目，如果需要则在此加上判断
	} else { //个人
		certTypeStr := c.DefaultPostForm("certType", "")
		typeId, err := strconv.Atoi(certTypeStr)
		if err != nil {
			c.JSON(http.StatusOK, model.Fail("证件类型不支持"))
			return
		}
		customer.CertType = entity.ECertificateType(typeId)
	}
	customer.IdNumber = c.DefaultPostForm("idNumber", "")
	global.Log.Info("UpdateCustomer: pareper to update", zap.Any("customer:", customer))

	err = dao.UpdateCustomer(customer)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("操作失败"))
		global.Log.Info("UpdateCustomer InsertCustomer failed.", zap.Any("error", err))
	} else {
		c.JSON(http.StatusOK, model.Success("更新成功"))
		//	c.JSON(http.StatusOK, model.SuccessObject("操作成功", customer))
		global.Log.Info("UpdateCustomer success.", zap.Any("new customer", customer))
	}
}

//********************************************
//Func  ：用户删除
//Method：DELETE
//Usage ：留言板页面-个人设置-账号注销
//********************************************
func DeleteCustomer(c *gin.Context) {
	customerIdStr := c.DefaultQuery("customerId", "")
	mobile := c.DefaultQuery("mobile", "")
	if customerIdStr == "" && mobile == "" {
		global.Log.Error("DeleteCustomer query customer detail failed, Error")
		c.JSON(http.StatusOK, model.Fail("未指定用户信息"))
		return
	}

	customerId, err := strconv.ParseInt(customerIdStr, 10, 64)
	if err != nil {
		customerId = -1
	}
	err = dao.DeleteCustom(mobile, customerId)
	global.Log.Info("DeleteCustomer :", zap.Any("mobile", mobile), zap.Any("customerId", customerId), zap.Any("error", err))
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("移除用户失败, "+err.Error()))
	} else {
		c.JSON(http.StatusOK, model.Success("删除成功"))
	}
}

//********************************************
//Func  ：绑定手机号到用户-校验密码
//Method：POST
//Usage ：留言板页面-个人设置-绑定手机-校验密码
//********************************************
func BindMobileToCustomerCheck(c *gin.Context) {
	customer := c.MustGet("USER").(*entity.Customer)
	req := entity.ReqBindMobileCustomerCheck{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("BindMobileToCustomerCheck ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密密码密文
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("BindMobileToCustomerCheck invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordByte, err := cryptoAsymmetric.DecryptWithBase64(req.Password, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("BindMobileToCustomerCheck sm2 decrypt password fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("密码无效"))
		return
	}
	// 校验密码是否正确
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("BindMobileToCustomerCheck invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordSum, err := cryptoDigest.Sum(passwordByte)
	if err != nil {
		global.Log.Error("BindMobileToCustomerCheck sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	if passwordSum != customer.Password {
		global.Log.Error("BindMobileToCustomerCheck invalid password")
		global.Log.Debug("BindMobileToCustomerCheck invalid password", zap.Any("password", req.Password))
		c.JSON(http.StatusUnauthorized, model.Fail("无效密码"))
		return
	}
	// 生成凭证，用于绑定是校验
	rsp := entity.RspBindMobileCustomerCheck{
		Evidence: uuid.NewV4().String(),
	}
	// 缓存凭证
	key := entity.RedisCustomerBindMobileEvidence + strconv.FormatInt(customer.Id, 10)

	if _, err = cache.TransBegin().
		Send("SET", key, rsp.Evidence).
		Send("EXPIRE", key, 60).Exec(); err != nil {
		global.Log.Error("BindMobileToCustomerCheck set fail",
			zap.Any("key", key),
			zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	c.JSON(http.StatusOK, model.SuccessObject("校验通过", rsp))
	return
}

//********************************************
//Func  ：绑定手机号到用户
//Method：POST
//Usage ：留言板页面-个人设置-绑定手机
//********************************************
func BindMobileToCustomer(c *gin.Context) {
	customer := c.MustGet("USER").(*entity.Customer)
	req := entity.ReqBindMobileCustomer{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("BindMobileToCustomer ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密密码密文
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("BindMobileToCustomer invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	mobileByte, err := cryptoAsymmetric.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("BindMobileToCustomer sm2 decrypt mobile fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("手机号无效"))
		return
	}
	req.Mobile = string(mobileByte)
	// 校验验证码
	if err = global.MsgCodeGenerator.Check(req.Mobile, req.Code); err != nil {
		global.Log.Error("BindMobileToCustomer invalid code", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail(err.Error()))
		return
	}
	// 校验凭证
	key := entity.RedisCustomerBindMobileEvidence + strconv.FormatInt(customer.Id, 10)
	evidence, err := redis.String(cache.Do("GET", key))
	if err != nil {
		global.Log.Error("BindMobileToCustomerCheck get fail",
			zap.Any("key", key),
			zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	if req.Evidence != evidence {
		global.Log.Error("BindMobileToCustomer ivalid evidence", zap.Any("evidence", req.Evidence))
		c.JSON(http.StatusBadRequest, model.Fail("凭证无效"))
		return
	}
	// 删除凭证
	_, err = cache.Do("DEL", key)
	if err != nil {
		global.Log.Error("BindMobileToCustomerCheck del fail",
			zap.Any("key", key),
			zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 更新手机号
	err = dao.UpdateCustomerMobile("", customer.Id, req.Mobile)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("操作失败"))
		return
	} else {
		c.JSON(http.StatusOK, model.Success("操作成功"))
		return
	}
}

//********************************************
//Func  ：绑定邮箱到用户
//Method：POST
//Usage ：留言板页面-个人设置-绑定邮箱
//********************************************
func BandingEmailToCustomer(c *gin.Context) {
	customerId, err := strconv.ParseInt(c.DefaultPostForm("customerId", ""), 10, 64)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("未指明用户ID"))
		return
	}

	mobile := strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if len(strings.TrimSpace(mobile)) <= 0 {
		c.JSON(http.StatusOK, model.Fail("手机号码不能为空"))
		return
	}

	email := strings.TrimSpace(c.DefaultPostForm("email", ""))
	if len(strings.TrimSpace(email)) <= 0 {
		c.JSON(http.StatusOK, model.Fail("邮箱不能为空"))
		return
	}

	msgVerCode := c.DefaultPostForm("msgVerCode", "")
	if len(strings.TrimSpace(msgVerCode)) <= 0 {
		c.JSON(http.StatusOK, model.Fail("请输入短信验证码"))
		return
	}

	err = dao.UpdateCustomerEmail(mobile, customerId, email)
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("操作失败"))
		return
	} else {
		c.JSON(http.StatusOK, model.Success("操作成功"))
		return
	}
}

//********************************************
//Func  ：通过邮件发送验证码
//Method：POST
//Usage ：留言板页面-个人设置-绑定邮箱
//********************************************
func RequireVercodeByEmail(c *gin.Context) {

	cryptoInstance, ok := global.Crypto.Symmetric[crypto.CryptoSM2]
	if ok {
		global.Log.Error("RequireVercodeByEmail crypto init failed.")
		c.JSON(http.StatusOK, model.Fail("请求参数异常"))
		return
	}

	emailStr := strings.TrimSpace(c.DefaultPostForm("email", ""))
	if len(strings.TrimSpace(emailStr)) <= 0 {
		global.Log.Error("RequireVercodeByEmail mail is null.")
		c.JSON(http.StatusOK, model.Fail("邮箱不能为空"))
		return
	}

	if false {
		mailByte, err := cryptoInstance.DecryptWithBase64(emailStr, global.ASymmetricPrivateKey)
		if err != nil {
			emailStr = string(mailByte) //此处mail已经变更为明文
		}
	}

	// 生成验证码
	verValue, err := global.MsgCodeGenerator.Gen("emailStr")
	if err != nil {
		global.Log.Error("global.MsgCodeGenerator failed.", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("发送验证码异常"))
		return
	}

	// 发送邮件
	address := []string{emailStr}
	err = email.SendGoMail(address, "", "DCE400服务平台绑定邮箱验证", "验证码"+verValue)
	if err != nil {
		global.Log.Error("RequireVercodeByEmail SendGoMail failed.", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("发送验证码异常"))
		return
	}

	// 返回相应
	c.JSON(http.StatusOK, model.Fail("已发送验证码到邮箱"))
}

//********************************************
//Func  ：用户修改密码
//Method：POST
//        手机号（密文）
//        用户id（密文）
//        新密码（密文）
//Usage ：留言板页面-个人设置-密码设置
//********************************************
func CustomerChangePassword(c *gin.Context) {
	customer := c.MustGet("USER").(*entity.Customer)
	req := entity.ReqChangePasswordCustomer{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("CustomerChangePassword ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密密码密文
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("CustomerChangePassword invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	oldByte, err := cryptoAsymmetric.DecryptWithBase64(req.OldPassword, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("CustomerChangePassword sm2 decrypt oldpassword fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("旧密码无效"))
		return
	}
	newByte, err := cryptoAsymmetric.DecryptWithBase64(req.NewPassword, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("CustomerChangePassword sm2 decrypt newpassword fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("新密码无效"))
		return
	}
	// 校验旧密码是否正确
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("CustomerChangePassword invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	oldpwdSum, err := cryptoDigest.Sum(oldByte)
	if err != nil {
		global.Log.Error("CustomerChangePassword sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	if oldpwdSum != customer.Password {
		global.Log.Error("CustomerChangePassword invalid oldPassword")
		c.JSON(http.StatusUnauthorized, model.Fail("无效旧密码"))
		return
	}
	// 校验新密码强度
	if !util.IsStrongPassword(string(newByte)) {
		c.JSON(http.StatusBadRequest, model.Fail("新密码强度不足"))
		return
	}
	// 新密码摘要
	pwdSum, err := cryptoDigest.Sum(newByte)
	if err != nil {
		global.Log.Error("CustomerChangePassword sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 更新密码
	err = dao.UpdateCustomerPassword("", customer.Id, pwdSum)
	if err != nil {
		c.JSON(http.StatusInternalServerError, model.Fail("操作失败"))
		return
	} else {
		c.JSON(http.StatusOK, model.Success("密码更新成功"))
		return
	}
}

//********************************************
//Func  ：用户找回密码（使用手机）,向手机号发送短信验证码，返回用户id（密文）。
//       下一步配合CustomerChangePassword接口修改系密码
//Method：POST
//Param ：设备号或唯一标识（密文）
//        手机号（密文）
//        图形验证码
//Usage ：
//********************************************
func RetrieveCustomerPasswordMsgVerCode(c *gin.Context) {
	customer := c.MustGet("USER").(*entity.Customer)
	req := entity.ReqRetrievePasswordMsgVerCodeCustomer{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("RetrieveCustomerPasswordMsgVerCode ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密密码密文
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("RetrieveCustomerPasswordMsgVerCode invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordByte, err := cryptoAsymmetric.DecryptWithBase64(req.Password, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("RetrieveCustomerPasswordMsgVerCode sm2 decrypt password fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("密码无效"))
		return
	}
	mobileByte, err := cryptoAsymmetric.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("RetrieveCustomerPasswordMsgVerCode sm2 decrypt mobile fail", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail("手机号无效"))
		return
	}
	req.Mobile = string(mobileByte)
	// 校验手机号（如果已绑定）
	if len(customer.Mobile) > 0 && req.Mobile != customer.Mobile {
		global.Log.Error("RetrieveCustomerPasswordMsgVerCode invalid mobile fail", zap.Any("db", customer.Mobile), zap.Any("req", req.Mobile))
		c.JSON(http.StatusBadRequest, model.Fail("手机号无效"))
		return
	}
	// 校验短信验证码
	if err := global.MsgCodeGenerator.Check(req.Mobile, req.Code); err != nil {
		global.Log.Error("RetrieveCustomerPasswordMsgVerCode MsgCodeGenerator.Check fail,", zap.Any("error", err))
		c.JSON(http.StatusBadRequest, model.Fail(err.Error()))
		return
	}

	// 校验密码强度
	if !util.IsStrongPassword(string(passwordByte)) {
		c.JSON(http.StatusOK, model.Fail("密码强度不足"))
		return
	}
	// 密码摘要
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("CustomerChangePassword invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordSum, err := cryptoDigest.Sum(passwordByte)
	if err != nil {
		global.Log.Error("CustomerChangePassword sm3 sum fail", zap.Any("error", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 更细密码
	err = dao.UpdateCustomerPassword("", customer.Id, string(passwordSum))
	if err != nil {
		c.JSON(http.StatusOK, model.Fail("操作失败"))
		return
	} else {
		c.JSON(http.StatusOK, model.Success("密码重置成功"))
		return
	}
}

//********************************************
//Func  ：用户短信验证码登录
//Method：POST
//Usage ：留言板页面-用户注册-信息变更
//********************************************
func LoginSmsCustomer(c *gin.Context) {
	req := entity.ReqSmsLoginCustomer{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("LoginSmsCustomer ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密手机号码
	cryptoInst, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("LoginSmsCustomer invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	mobileByte, err := cryptoInst.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("LoginSmsCustomer invalid sm2.Decrypt", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	req.Mobile = string(mobileByte)
	// 确认用户已注册
	customer, err := dao.QueryCustomerDetail(req.Mobile, 0)
	if err != nil {
		global.Log.Error("LoginSmsCustomer no found this custom.", zap.Any("mobile", req.Mobile))
		c.JSON(http.StatusBadRequest, model.Fail("未找到该用户信息"))
		return
	}
	// 校验验证码
	if err = global.MsgCodeGenerator.Check(req.Mobile, req.Code); err != nil {
		global.Log.Error("LoginSmsCustomer MsgCodeGenerator.Check fail,", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail(err.Error()))
		return
	}
	// 生成token
	token, err := util.GetJwtToken(customer.Id, config.Token().Expire, config.Token().Key)
	if err != nil {
		global.Log.Error("LoginSmsCustomer GetJwtToken failed.", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	log := entity.CustomerLoginLog{
		Id:         util.GenSnowflakeInt64(),
		CustomerId: customer.Id,
		LoginTime:  time.Now(),
		LoginType:  entity.LoginType_MobileVercode,
		Token:      token,
	}
	err = dao.InsertCustmoerLoginLog(log)
	if err != nil {
		global.Log.Warn("LoginSmsCustomer.InsertCustmoerLoginLog failed.", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	rsp := entity.RspLoginCustomer{
		Token:  token,
		Expire: config.Token().Expire,
	}
	c.JSON(http.StatusOK, model.SuccessObject("登录成功", rsp))
	global.Log.Info("LoginSmsCustomer success.", zap.Any("token", rsp))
}

//********************************************
//Func  ：用户登录
//Method：POST
//Usage ：留言板页面-用户注册-信息变更
//********************************************
func LoginCustomer(c *gin.Context) {
	req := entity.ReqPwdLoginCustomer{}
	if err := c.ShouldBind(&req); err != nil {
		global.Log.Error("LoginCustomer ShouldBind fail", zap.Any("err", err))
		c.JSON(http.StatusBadRequest, model.Fail("参数错误"))
		return
	}
	// 解密手机号码
	cryptoAsymmetric, ok := global.Crypto.Asymmetric[crypto.CryptoSM2]
	if !ok {
		global.Log.Error("LoginSmsCustomer invalid crypto", zap.Any("type", crypto.CryptoSM2))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	mobileByte, err := cryptoAsymmetric.DecryptWithBase64(req.Mobile, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("LoginCustomer invalid sm2.Decrypt", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	req.Mobile = string(mobileByte)
	// 解密密码
	passwordByte, err := cryptoAsymmetric.DecryptWithBase64(req.Password, global.ASymmetricPrivateKey)
	if err != nil {
		global.Log.Error("LoginCustomer invalid sm2.Decrypt", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	// 获取密码摘要
	cryptoDigest, ok := global.Crypto.Digest[crypto.CryptoSM3]
	if !ok {
		global.Log.Error("LoginCustomer invalid crypto", zap.Any("type", crypto.CryptoSM3))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	passwordDigest, err := cryptoDigest.Sum(passwordByte)
	if err != nil {
		global.Log.Error("LoginCustomer invalid sm3.Sum", zap.Any("err", err))
		c.JSON(http.StatusInternalServerError, model.Fail("内部错误"))
		return
	}
	req.Password = string(passwordDigest)
	// 校验密码
	customer, err := dao.QueryCustomerDetail(req.Mobile, 0)
	if err != nil {
		global.Log.Error("LoginCustomer no found this custom.")
		c.JSON(http.StatusOK, model.Fail("未找到该用户信息"))
		return
	}
	if req.Password != customer.Password {
		global.Log.Error("LoginCustomer invalid password")
		global.Log.Debug("LoginCustomer invalid password", zap.Any("password", req.Password))
		c.JSON(http.StatusOK, model.Fail("密码错误"))
		return
	}
	// 生成token
	token, err := util.GetJwtToken(customer.Id, config.Token().Expire, config.Token().Key)
	if err != nil {
		global.Log.Error("LoginCustomer.GetJwtToken failed.", zap.Any("error", err))
	}
	// 记录login_log
	log := entity.CustomerLoginLog{
		Id:         util.GenSnowflakeInt64(),
		CustomerId: customer.Id,
		LoginTime:  time.Now(),
		LoginType:  entity.LoginType_MobilePwd,
		Token:      token,
	}
	if err = dao.InsertCustmoerLoginLog(log); err != nil {
		global.Log.Error("LoginCustomer.InsertCustmoerLoginLog failed.", zap.Any("error", err))
		c.JSON(http.StatusOK, model.Fail("登陆失败"))
		return
	}
	rsp := entity.RspLoginCustomer{
		Token:  token,
		Expire: config.Token().Expire,
	}
	c.JSON(http.StatusOK, model.SuccessObject("登录成功", rsp))
	global.Log.Info("LoginCustomer success.", zap.Any("new token", rsp))
}

//********************************************
//Func  ：用户登出
//Method：POST
//Usage ：留言板页面-用户注册-信息变更？？ 30天自动logout
//********************************************
func LogoutCustomer(c *gin.Context) {
	//todo ，del token吗
	var log entity.CustomerLoginLog
	mobile := strings.TrimSpace(c.DefaultPostForm("mobile", ""))
	if mobile == "" {
		global.Log.Error("LogoutOperator mobile is none.")
		c.JSON(http.StatusOK, model.Fail("用户信息错误"))
		return
	}
	log.CustomerId, _ = strconv.ParseInt(c.DefaultPostForm("customerId", ""), 10, 64)
	log.LogoutTime = time.Now()
	log.LoginOrLogout = 1
	dao.InsertCustmoerLoginLog(log)
	c.JSON(http.StatusOK, model.SuccessObject("登出成功", ""))

}
